﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using DiscoveryLogic.Analytics.DataObjects;
using DiscoveryLogic.Analytics.DataObjects.Visuals;

namespace DiscoveryLogic.Analytics.Diagrams.Physics
{
	public class ParticleEventArgs:RoutedEventArgs
	{
		private Particle _Particle;
		/// <summary></summary>
		public Particle Particle
		{
			get
			{
				return this._Particle;
			}
			set
			{
				this._Particle = value;
			}
		}

		public ParticleEventArgs(RoutedEvent routedEvent, Particle particle):base(routedEvent)
		{
			this._Particle = particle;	
		}
	}

	public class GravityParticleCanvas : Canvas
	{
		#region events 
		/// <summary>
		/// 
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public delegate void ParticleReleasedEventHandler(object sender, ParticleEventArgs e);

		public static readonly RoutedEvent ParticleReleasedEvent =
			EventManager.RegisterRoutedEvent("ParticleReleased",
			                                 RoutingStrategy.Bubble,
			                                 typeof (ParticleReleasedEventHandler),
			                                 typeof (GravityParticleCanvas));

		public event ParticleReleasedEventHandler ParticleReleased
		{
			add { AddHandler(ParticleReleasedEvent, value); }
			remove { RemoveHandler(ParticleReleasedEvent, value); }
		}

		private void FireParticleReleasedEvent(Particle particle)
		{
			ParticleEventArgs args = new ParticleEventArgs(ParticleReleasedEvent, particle);
			RaiseEvent(args);
		}

	    public delegate void OnParticleSelected(Particle particle);

	    public event OnParticleSelected ParticleSelected;
		#endregion

		#region fields
		private readonly DispatcherTimer _WorldTimer=new DispatcherTimer();
		private Point _OwnerWindowPosition;
		private Point _PrevAbsoluteMousePosition;
		private Particle _SelectedParticle = null;
		private double _SelectedParticleMass = 0.0f;
		private int _ZIndex = 0;
	    private int _Iteration = 0;
		#endregion

		#region props
        private string _NetworkName;
        /// <summary></summary>
        public string NetworkName
        {
            get
            {
                return this._NetworkName;
            }
            set
            {
                this._NetworkName = value;
            }
        }

        private NetworkVisual _NetVisual =NetworkVisual.Default;
        /// <summary></summary>
        public NetworkVisual NetVisual
        {
            get
            {
                return this._NetVisual;
            }
            set
            {
                this._NetVisual = value;
            }
        }

        private bool _IsStopped;
        /// <summary></summary>
        public bool IsStopped
        {
            get
            {
                return this._IsStopped;
            }
            set
            {
                if (this._IsStopped != value)
                {
                    this._IsStopped = value;
                }
                if(this._WorldTimer.IsEnabled && this._IsStopped)
                {
                    this._WorldTimer.IsEnabled = false;
                }
            }
        }

		private ParticleSystem _ParticleSystem;
		/// <summary></summary>
		public ParticleSystem ParticleSystem
		{
			get
			{
				return this._ParticleSystem;
			}
			set
			{
				this._ParticleSystem = value;
			}
		}

		private bool _RenderParticleSystem;
		/// <summary></summary>
		public bool RenderParticleSystem
		{
			get
			{
				return this._RenderParticleSystem;
			}
			set
			{
				this._RenderParticleSystem = value;
			}
		}

		private Window _OwnerWindow;
		/// <summary></summary>
		public Window OwnerWindow
		{
			get
			{
				return this._OwnerWindow;
			}
			set
			{
				if(value !=null)
				{
					this._OwnerWindow = value;
					this._OwnerWindowPosition=new Point(0,0);
					this._OwnerWindow.LocationChanged += new EventHandler(_OwnerWindow_LocationChanged);
				}
			}
		}

		void _OwnerWindow_LocationChanged(object sender, EventArgs e)
		{
			Vector deltaMovement = new Vector(this._OwnerWindowPosition.X - this._OwnerWindow.Left, 
				this._OwnerWindowPosition.Y - this._OwnerWindow.Top);
			this._OwnerWindowPosition=new Point(this._OwnerWindow.Left, this._OwnerWindow.Top);
			foreach(Particle particle in this.ParticleSystem.Particles)
			{
				particle.MoveToPosition(deltaMovement);
			}
		}
		#endregion

		#region ctor
		public GravityParticleCanvas()
		{
			this._WorldTimer.Interval = new TimeSpan(50);
			this._WorldTimer.Tick += new EventHandler(_WorldTimer_Tick);
			this._ParticleSystem=new ParticleSystem();
		}
		#endregion

		#region layout 
		protected override Size ArrangeOverride(Size arrangeSize)
		{
			foreach(UIElement element in this.InternalChildren)
			{
				double left = Canvas.GetLeft(element);
				double top = Canvas.GetTop(element);
				double x = double.IsNaN(left) ? 0 : left;
				double y = double.IsNaN(top) ? 0 : top;
				element.Arrange(new Rect(new Point(x,y),element.DesiredSize));
			}
			return arrangeSize;
		}

		protected override Size MeasureOverride(Size constraint)
		{
			Size size = new Size(double.PositiveInfinity, double.PositiveInfinity);
			foreach(UIElement element in this.InternalChildren)
			{
				element.Measure(size);
			}
			return new Size();
		}

		protected override void OnRender(System.Windows.Media.DrawingContext dc)
		{
			base.OnRender(dc);
			this.ParticleSystem.Render(dc);
		}
		#endregion

		#region drag-drop
		public void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
		{
			if(e.ButtonState==MouseButtonState.Released)
			{
				if(this._SelectedParticle !=null)
				{
					this._SelectedParticle.Mass = this._SelectedParticleMass;
					this.FireParticleReleasedEvent(this._SelectedParticle);
                    if (this._IsStopped)
                    {
                        this._SelectedParticle.SnapControl();
                        this.InvalidateVisual();
                    }
				}
			}
		}

		public void Canvas_MouseMove(object sender, MouseEventArgs e)
		{
			if(e.LeftButton==MouseButtonState.Pressed)
			{
				if(this._SelectedParticle!=null)
				{
					Point absolutePosition = Mouse.GetPosition(this);
					Rect constraintRect = new Rect(0,0,this.ActualWidth,this.ActualHeight);
					this._SelectedParticle.SetPosition(
						new Vector( 
							this._SelectedParticle.Position.X + (absolutePosition.X - this._PrevAbsoluteMousePosition.X),
							this._SelectedParticle.Position.Y + (absolutePosition.Y - this._PrevAbsoluteMousePosition.Y)),
						constraintRect);
                    if (this._IsStopped)
                    {
                        this._SelectedParticle.SnapControl();
                        this.InvalidateVisual();
                    }
					this._PrevAbsoluteMousePosition = absolutePosition;
				}
			}
		}

		public void Canvas_MouseDown(object sender, MouseEventArgs e)
		{
			if(e.LeftButton==MouseButtonState.Pressed)
			{
				this._PrevAbsoluteMousePosition = Mouse.GetPosition(this);
				var particleWhere = from particle in this.ParticleSystem.Particles
				                    where particle.Control == sender
				                    select particle;
				if(particleWhere.Count()>0)
				{
					Particle particle = particleWhere.First();
					if(this._SelectedParticle!=null)
						this._SelectedParticle.Mass = this._SelectedParticleMass;
					this._SelectedParticleMass = particle.Mass;
					this._SelectedParticle = particle;
					this._SelectedParticle.Mass = Single.PositiveInfinity;
					this._SelectedParticle.Velocity=new Vector();
					this._SelectedParticle.Control.SetValue(Canvas.ZIndexProperty, this._ZIndex++);

                    if (this.ParticleSelected != null)
                        this.ParticleSelected(this._SelectedParticle);

					return;
				}
			}
		}

		public void Canvas_MouseEnter(object sender, MouseEventArgs e)
		{
			(sender as Control).SetValue(Canvas.ZIndexProperty, this._ZIndex++);
		}
		#endregion

		#region simulation 
		public void StartSimulation()
		{
            if (!this._WorldTimer.IsEnabled)
            {
                this._IsStopped = false;
                this._Iteration = 0;
                this._WorldTimer.IsEnabled = true;
            }
		}

		public void StopSimulation()
		{
			this._WorldTimer.IsEnabled = false;
		}

		void _WorldTimer_Tick(object sender, EventArgs e)
		{
		    this._Iteration++;
            
			Rect constraintRect = new Rect(0, 0, this.ActualWidth, this.ActualHeight);
			lock (this._ParticleSystem.Particles)
			{
				int particleMoved = this.ParticleSystem.DoEulerStep(0.005f, constraintRect);
                if (particleMoved == 0 && this._Iteration>200)
                {
                    this.IsStopped = true;
                }
                foreach (Particle particle in this.ParticleSystem.Particles)
                {
                    particle.SnapControl();
                }
			}
			this.InvalidateVisual();
		}
		#endregion

        #region select 
        public void SelectEntity(EntityBase entity)
        {
            
        }
        #endregion
    }
}
